/**
 * Software License, Version 1.0
 * 
 * Copyright 2003 The Trustees of Indiana University.  All rights reserved.
 * 
 *
 *Redistribution and use in source and binary forms, with or without 
 *modification, are permitted provided that the following conditions are met:
 *
 *1) All redistributions of source code must retain the above copyright notice,
 * the list of authors in the original source code, this list of conditions and
 * the disclaimer listed in this license;
 *2) All redistributions in binary form must reproduce the above copyright 
 * notice, this list of conditions and the disclaimer listed in this license in
 * the documentation and/or other materials provided with the distribution;
 *3) Any documentation included with all redistributions must include the 
 * following acknowledgement:
 *
 *"This product includes software developed by the Community Grids Lab. For 
 * further information contact the Community Grids Lab at 
 * http://communitygrids.iu.edu/."
 *
 * Alternatively, this acknowledgement may appear in the software itself, and 
 * wherever such third-party acknowledgments normally appear.
 * 
 *4) The name Indiana University or Community Grids Lab or NaradaBrokering, 
 * shall not be used to endorse or promote products derived from this software 
 * without prior written permission from Indiana University.  For written 
 * permission, please contact the Advanced Research and Technology Institute 
 * ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202.
 *5) Products derived from this software may not be called NaradaBrokering, 
 * nor may Indiana University or Community Grids Lab or NaradaBrokering appear
 * in their name, without prior written permission of ARTI.
 * 
 *
 * Indiana University provides no reassurances that the source code provided 
 * does not infringe the patent or any other intellectual property rights of 
 * any other entity.  Indiana University disclaims any liability to any 
 * recipient for claims brought by any other entity based on infringement of 
 * intellectual property rights or otherwise.  
 *
 *LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO 
 *WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES
 *NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF 
 *INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS. 
 *INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS", 
 *"VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.  
 *LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR 
 *ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION 
 *GENERATED USING SOFTWARE.
 */
package cgl.narada.service.replay.impl;

import java.util.Hashtable;

import cgl.narada.matching.Profile;
import cgl.narada.service.ServiceException;
import cgl.narada.service.qos.impl.QosServiceImpl;
import cgl.narada.service.replay.EntityReplayActuator;
import cgl.narada.service.replay.ReplayRequest;
import cgl.narada.service.replay.ReplayServiceListener;
import cgl.narada.util.UUIDRetriever;

/*
 The Entity Replay Actuator which initiates replays and assorted 
 functionalities associated with replay services. 
 
 @author Shrideep Pallickara and Hasan Bulut
 $Date$
 $Revision$
 */

public class EntityReplayActuatorImpl extends EntityReplayActuator {
	private UUIDRetriever								retriever;
	private Hashtable										replayRequests;
	private Hashtable										replayActuators;

	private String											moduleName	= "EntityReplayActuatorImpl: ";

	private static EntityReplayActuator	instance		= new EntityReplayActuatorImpl();

	private QosServiceImpl							qosServiceImpl;

	private EntityReplayActuatorImpl() {
		retriever = UUIDRetriever.getInstance();
		replayRequests = new Hashtable();
		replayActuators = new Hashtable();
	}

	public static EntityReplayActuator getInstance() {
		return instance;
	}

	/** Create a replay request based on the specified constraints */
	public ReplayRequest createReplayRequest(int templateId, int entityId,
			long[] sequencesToReplay) throws ServiceException {
		checkTemplateAndEntity(templateId, entityId);

		if (sequencesToReplay == null) {
			throw new ServiceException(moduleName
					+ "The specified replay sequences are null");
		}

		String replayIdentifier = createReplayIdentifer();

		ReplayRequest replayRequest = new ReplayRequestEvent(templateId, entityId,
				replayIdentifier, sequencesToReplay);
		return replayRequest;
	}

	/** Create a replay request based on the specified constraints */
	public ReplayRequest createReplayRequest(int templateId, int entityId,
			long sequenceStart, long sequenceEnd) throws ServiceException {

		checkTemplateAndEntity(templateId, entityId);

		if (sequenceEnd != 0 && sequenceStart > sequenceEnd) {
			throw new ServiceException(moduleName + "Specified sequence start="
					+ sequenceStart + " is GREATER THAN sequence" + " end=" + sequenceEnd);
		}

		String replayIdentifier = createReplayIdentifer();

		ReplayRequest replayRequest = new ReplayRequestEvent(templateId, entityId,
				replayIdentifier, sequenceStart, sequenceEnd);
		return replayRequest;

	}

	/** Create a replay request based on the specified constraints */
	public ReplayRequest createReplayRequest(int templateId, int entityId,
			long sequenceStart, long sequenceEnd, Profile[] specifiedConstraints)
			throws ServiceException {

		checkTemplateAndEntity(templateId, entityId);

		if (sequenceEnd != 0 && sequenceStart > sequenceEnd) {
			throw new ServiceException(moduleName + "Specified sequence start="
					+ sequenceStart + " is GREATER THAN sequence" + " end=" + sequenceEnd);
		}

		if (specifiedConstraints != null) {
			throw new ServiceException(moduleName + "The specified constraints for "
					+ "replay == NULL");
		}

		String replayIdentifier = createReplayIdentifer();

		ReplayRequest replayRequest = new ReplayRequestEvent(templateId, entityId,
				replayIdentifier, sequenceStart, sequenceEnd, specifiedConstraints);
		return replayRequest;
	}

	// Request for timestamp
	public ReplayRequest createReplayRequest(int templateId, int entityId,
			boolean requestForTimestamp, long sequenceNumber) throws ServiceException {

		String replayIdentifier = createReplayIdentifer();
		ReplayRequest replayRequest = new ReplayRequestEvent(templateId, entityId,
				replayIdentifier, requestForTimestamp, sequenceNumber);
		return replayRequest;
	}

	// request for replay from a sequence number to a specified timestamp
	public ReplayRequest createReplayRequest(int templateId, int entityId,
			String replayRequestIdentifier, String replayTopic,
			boolean hasStartingSequenceAndEndingTimestamp, long startingSequence,
			long endingTimestamp) throws ServiceException {

		String replayIdentifier = createReplayIdentifer();
		ReplayRequest replayRequest = new ReplayRequestEvent(templateId, entityId,
				replayRequestIdentifier, replayTopic,
				hasStartingSequenceAndEndingTimestamp, startingSequence,
				endingTimestamp);
		return replayRequest;
	}

	// if replayWithTimestampRange set to true, replays the timestamp range
	public ReplayRequest createReplayRequest(int templateId, int entityId,
			String replayTopic, long startTimestamp, long endTimestamp,
			boolean replayWithTimestampRange) throws ServiceException {

		String replayIdentifier = createReplayIdentifer();
		ReplayRequest replayRequest = new ReplayRequestEvent(templateId, entityId,
				replayIdentifier, replayTopic, startTimestamp, endTimestamp,
				replayWithTimestampRange);
		return replayRequest;
	}

	// if hasTimestampRange set to true, brings the sequence numbers and 
	// corresponding timestamps that falls into the range
	public ReplayRequest createReplayRequest(int templateId, int entityId,
			long startTimestamp, long endTimestamp, boolean hasTimestampRange)
			throws ServiceException {

		String replayIdentifier = createReplayIdentifer();
		ReplayRequest replayRequest = new ReplayRequestEvent(templateId, entityId,
				replayIdentifier, startTimestamp, endTimestamp, hasTimestampRange);
		return replayRequest;
	}

	// if requestSequencesAndTimestamps set to true, asks for all of the events
	// under that template id with sequences and timestamps
	public ReplayRequest createReplayRequest(int templateId, int entityId,
			boolean requestSequencesAndTimestamps) throws ServiceException {

		String replayIdentifier = createReplayIdentifer();
		ReplayRequest replayRequest = new ReplayRequestEvent(templateId, entityId,
				replayIdentifier, requestSequencesAndTimestamps);
		return replayRequest;
	}

	// replay to the specified topic   
	public ReplayRequest createReplayRequest(int templateId, int entityId,
			String replayTopic, long[] sequencesToReplay) throws ServiceException {
		checkTemplateAndEntity(templateId, entityId);

		if (sequencesToReplay == null) {
			throw new ServiceException(moduleName
					+ "The specified replay sequences are null");
		}

		if (replayTopic == null) {
			throw new ServiceException(moduleName
					+ "The specified replay topic is null");
		}

		String replayIdentifier = createReplayIdentifer();

		ReplayRequest replayRequest = new ReplayRequestEvent(templateId, entityId,
				replayIdentifier, replayTopic, sequencesToReplay);
		return replayRequest;
	}

	// replay from sequenceStart to sequenceEnd
	public ReplayRequest createReplayRequest(int templateId, int entityId,
			String replayTopic, long sequenceStart, long sequenceEnd)
			throws ServiceException {

		checkTemplateAndEntity(templateId, entityId);

		if (sequenceEnd != 0 && sequenceStart > sequenceEnd) {
			throw new ServiceException(moduleName + "Specified sequence start="
					+ sequenceStart + " is GREATER THAN sequence" + " end=" + sequenceEnd);
		}

		if (replayTopic == null) {
			throw new ServiceException(moduleName
					+ "The specified replay topic is null");
		}

		String replayIdentifier = createReplayIdentifer();

		ReplayRequest replayRequest = new ReplayRequestEvent(templateId, entityId,
				replayIdentifier, replayTopic, sequenceStart, sequenceEnd);
		return replayRequest;
	}

	//replay from sequenceStart to sequenceEnd with specified constraints
	public ReplayRequest createReplayRequest(int templateId, int entityId,
			String replayTopic, long sequenceStart, long sequenceEnd,
			Profile[] specifiedConstraints) throws ServiceException {

		checkTemplateAndEntity(templateId, entityId);

		if (sequenceEnd != 0 && sequenceStart > sequenceEnd) {
			throw new ServiceException(moduleName + "Specified sequence start="
					+ sequenceStart + " is GREATER THAN sequence" + " end=" + sequenceEnd);
		}

		if (specifiedConstraints != null) {
			throw new ServiceException(moduleName + "The specified constraints for "
					+ "replay == NULL");
		}

		if (replayTopic == null) {
			throw new ServiceException(moduleName
					+ "The specified replay topic is null");
		}

		String replayIdentifier = createReplayIdentifer();

		ReplayRequest replayRequest = new ReplayRequestEvent(templateId, entityId,
				replayIdentifier, replayTopic, sequenceStart, sequenceEnd,
				specifiedConstraints);
		return replayRequest;
	}

	public void setQosService(QosServiceImpl qosServiceImpl) {
		this.qosServiceImpl = qosServiceImpl;
	}

	/** Check to see if replay is possible for a given templateId and entityId*/
	public void checkIfReplayPossible(int templateId, int entityId)
			throws ServiceException {

	}

	private void checkTemplateAndEntity(int templateId, int entityId)
			throws ServiceException {

		if (templateId == 0 && entityId == 0) {
			throw new ServiceException(moduleName + "Invalid template [" + templateId
					+ "] and entity [" + entityId + "] specified.");
		}

		if (templateId == 0) {
			throw new ServiceException(moduleName + "Invalid template [" + templateId
					+ "] specified.");
		}

		if (entityId == 0) {
			throw new ServiceException(moduleName + "Invalid entity [" + entityId
					+ "] specified.");
		}
	}

	private String createReplayIdentifer() throws ServiceException {
		if (retriever == null) {
			throw new ServiceException(moduleName
					+ "Unable to initialize UUID generation.");
		}

		String replayIdentifier = retriever.getRandomBasedUUIDAsString();

		if (replayIdentifier == null) {
			throw new ServiceException(moduleName
					+ "Replay identifier generation failed.");
		}

		return replayIdentifier;
	}

	/** Retrieve a previously created replay request */
	public ReplayRequest getReplayRequest(String replayIdentifier)
			throws ServiceException {
		if (replayRequests.containsKey(replayIdentifier)) {
			return (ReplayRequest) replayRequests.get(replayIdentifier);
		}

		throw new ServiceException(moduleName + "Replay Request corresponding "
				+ "to -> " + replayIdentifier + " not available");
	}

	/** Initiate replay based on the create replay request */
	public void initiateReplay(ReplayRequest replayRequest,
			ReplayServiceListener replayListener) throws ServiceException {
		int entityId = replayRequest.getEntityId();
		Object entityKey = new Integer(entityId);

		if (!replayActuators.containsKey(entityKey)) {
			ReplayActuator replayActuator = new ReplayActuator(entityId,
					qosServiceImpl);
			replayActuators.put(entityKey, replayActuator);
		}

		ReplayActuator replayActuator = (ReplayActuator) replayActuators
				.get(entityKey);
		replayActuator.initiateReplay(replayRequest, replayListener);
	}

	public void replayToTopic(ReplayRequest replayRequest,
			ReplayServiceListener replayListener) throws ServiceException {
		int entityId = replayRequest.getEntityId();
		Object entityKey = new Integer(entityId);

		if (!replayActuators.containsKey(entityKey)) {
			ReplayActuator replayActuator = new ReplayActuator(entityId,
					qosServiceImpl);
			replayActuators.put(entityKey, replayActuator);
		}

		ReplayActuator replayActuator = (ReplayActuator) replayActuators
				.get(entityKey);
		replayActuator.replayToTopic(replayRequest, replayListener);
	}

}